#include "Strategy.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <iostream>
using namespace std;

bool Strategy::init(const nlohmann::json config)
{
	REAL = config["REAL"].get<int>();
	START_TIME_ =  Timer::str2nano(config["START_TIME"].get<string>().c_str());
	fprintf(stderr,"%s \n", Timer::nano2str(START_TIME_).c_str());
	strcpy(MARKET_DATA,  config["MARKET_DATA"].get<string>().c_str());
	char TRADE_RECORD[32];
	strcpy(TRADE_RECORD, config["TRADE_RECORD"].get<string>().c_str());
	nsymbol_ = config["SYMBOL"].size();
    for(auto& p:config["TRADING_PERIOD"])
		trading_period.push_back(make_pair<int, int>(1000 * p[0].get<int>(), 1000 * p[1].get<int>()));
	cache_ = new Ticker[nsymbol_];
	for(int idx=0;idx<nsymbol_;idx++)
	{
		Ticker t;
		memset(&t, 0, sizeof(Ticker));
		t.idx = idx;
		strcpy(t.name,config["SYMBOL"][idx]["name"].get<string>().c_str());
		t.position = config["SYMBOL"][idx]["position"].get<int>();
		t.margin = 0;
		t.position_nano = 0;
		t.last = Snapshot();
		memcpy(cache_ + idx, &t, sizeof(Ticker));
		log_name_ += string(t.name) + "_";
		fprintf(stdout,"%d: %s pos:%d\n", idx, t.name, t.position);
	}
	for(const auto& i:config["params"].items())
		log_name_ += "_"+i.key() + "_" + to_string(i.value());
 	log_name_  += ".log";
	on_init(config["params"]);
	if(REAL)
	{
		int ft = open(TRADE_RECORD,O_RDWR);
		Order tmp;
		memset(&tmp,0,sizeof(Snapshot));
		if(ft == -1)
		{
			int fw=open(TRADE_RECORD, O_WRONLY|O_CREAT|O_TRUNC,00644);
			for(long i=0;i<OrderCapacity; i++)
				int r=write(fw,&tmp,sizeof(Order));
			close(fw);
			fprintf(stderr,"create %s, size: %ldMB\n",TRADE_RECORD, sizeof(Order)*OrderCapacity/1024/1024);
			ft = open(TRADE_RECORD,O_RDWR);
		}
		orders_ = (Order*)mmap(NULL,sizeof(Order)*OrderCapacity, PROT_READ|PROT_WRITE,MAP_SHARED, ft, 0);
		close(ft);
		for(int i = 0;i<OrderCapacity;i++)
		{
			if((orders_ + i)->order_nano == 0)
			{
				order_id_ = i;
				break;
			}
		}
		fprintf(stderr,"trade id start from %d\n",order_id_);
	}
	return true;
}

void Strategy::send_order(int volume,char offset,int idx)
{
	if(volume==0)
		return;
	char direction = volume > 0 ? 'B':'S';
	Ticker* t = cache_ + idx;
	const Snapshot* cur = &(t->last);
	double order_price   = (volume > 0 ? cur->AskPrice1:cur->BidPrice1)*1e-3;
	if(real_time_)
	{
		Order o;
		memset(&o,0,sizeof(Order));
		strcpy(o.symbol, t->name);
		o.order_price   = order_price;
		o.entrust_price = (volume > 0 ? cur->UpperLimitPrice:cur->LowerLimitPrice)*1e-3;
		o.target_volume = abs(volume);
		o.direction = direction;
		o.offset = offset;
		o.id = order_id_;
		o.md_nano = md_nano_;
		o.process_nano = cur_nano_;
		o.order_nano = Timer::getNano();
		memcpy(orders_ + order_id_, &o, sizeof(Order));
		printf("send id:%d %s\n", order_id_, o.symbol);
		order_id_++;
	}
	double cost= 0;
	if(offset == OFFSET_OPEN)
	{
		t->margin += fabs(volume * order_price);
		t->position_nano = md_nano_;
	}
	else
	{
		cost =  fabs(t->margin / t->position); 
		t->pnl += volume * (cost - order_price );
		t->margin -= fabs(volume * cost); 
	}
	t->position += volume;
	printf("%s %s %c%c %8.3f cost:%10.2f tot_pnl:%10.2f\n",Timer::nano2str(md_nano_).c_str(), t->name, direction,offset, order_price,  cost, t->pnl);
}

bool Strategy::is_trading(int t )
{
	bool ret = false;
	for(const pair<int,int>& p:trading_period)
	{
		if(t >= p.first && t<= p.second)
		{
			ret = true;
			break;
		}
	}
	return ret;
}

void Strategy::process(int idx, const Snapshot* data)
{
	Ticker* cache = cache_ + idx;	
	if(cache->last.nano >= md_nano_ || data->Time == 0)
		return;
	memcpy(&cache->last, data, sizeof(Snapshot));
	on_process(cache);
}

void Strategy::run()
{
	std::map<long, std::pair<int,Snapshot> > time_line_;
	vector<long> cur(nsymbol_);
	vector<long> loc(nsymbol_);
	long count = 0;
	for(int idx = 0; idx < nsymbol_; idx++)
	{
		string fn = MARKET_DATA + string((cache_+idx)->name) + ".bin";
		fprintf(stdout,"read %s \n", fn.c_str());
		FILE* fp = fopen(fn.c_str(),"r");
		fseek(fp,0,SEEK_END);
		long len = ftell(fp) / sizeof(Snapshot);
		Snapshot data;
		for(long i = 0;i < len; i++)
		{
			fseek(fp, i*sizeof(Snapshot),SEEK_SET);
			fread(&data, sizeof(Snapshot),1, fp);
			//fprintf(stderr,"%s\n", Timer::nano2str(data.nano).c_str());
			if(data.nano == 0)
			{
				cur[idx] = i;
				break;
			}
			if(data.nano < START_TIME_)
				continue;
			if( !is_trading(data.Time) )
				continue;
			auto item = pair<int,Snapshot>();
			item.first = idx;
			item.second = data;
			time_line_[data.nano] = item;
			count++;
			if(count % 100000 == 0)
				fprintf(stdout, "%ld\n",count);
		}
		fclose(fp);
	}
	for(const auto& it:time_line_)
	{
		int idx = it.second.first;
		const Snapshot* s = &it.second.second;
		md_nano_ = s->nano;
		count--;
		if(count ==0)
			print_log_ = true;
		process(idx, s);
	}
	time_line_.clear();
	if(REAL)
	{
		Snapshot** data	= new Snapshot*[nsymbol_];
		for(int idx = 0; idx < nsymbol_; idx++)
		{
			loc[idx] = cur[idx] % BLOCK;
			string fn = MARKET_DATA + string((cache_+idx)->name) + ".bin";
			int fh  =open(fn.c_str(), O_RDONLY);
			data[idx] = (Snapshot*)mmap(NULL, sizeof(Snapshot)*BLOCK*2, PROT_READ, MAP_SHARED,fh, sizeof(Snapshot)*(cur[idx] - cur[idx] % BLOCK));
			close(fh);
		}
		fprintf(stdout,"start\n");
		real_time_ = true;
		while(1)
		{
			for(int idx = 0; idx < nsymbol_; idx++)
			{
				Snapshot* s = data[idx] + loc[idx];
				if(s->nano > 0)
				{
					cur_nano_ = Timer::getNano(); 
					md_nano_ = s->nano;
					process(idx, s);
					loc[idx] += 1;
					if(loc[idx] == 2*BLOCK)
					{
						loc[idx] = 0;
						cur[idx] = cur[idx] - cur[idx] % BLOCK + 2*BLOCK;
						string fn = MARKET_DATA + string((cache_+idx)->name) + ".bin";
						int fh  =open(fn.c_str(), O_RDONLY);
						data[idx] = (Snapshot*)mmap(NULL, sizeof(Snapshot)*BLOCK*2, PROT_READ, MAP_SHARED,fh, sizeof(Snapshot)*cur[idx]);
						close(fh);

					}
				}
			}
		}
	}
}


